﻿namespace UnityEditor.VisualStudioIntegration
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Security;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Xml;
    using UnityEditor;
    using UnityEditor.Scripting;
    using UnityEditorInternal;

    internal class SolutionSynchronizer
    {
        private static readonly Regex _MonoDevelopPropertyHeader;
        private Dictionary<string, string> _pathCache;
        private readonly string _projectDirectory;
        private readonly string _projectName;
        private readonly ISolutionSynchronizationSettings _settings;
        [CompilerGenerated]
        private static Func<MonoIsland, bool> <>f__am$cacheD;
        private static readonly string DefaultMonoDevelopSolutionProperties;
        public static readonly ISolutionSynchronizationSettings DefaultSynchronizationSettings = new DefaultSolutionSynchronizationSettings();
        public static readonly string MSBuildNamespaceUri;
        private static readonly Dictionary<ScriptingLanguage, string> ProjectExtensions;
        public static readonly Regex scriptReferenceExpression;
        private static readonly Dictionary<string, ScriptingLanguage> SupportedExtensions;
        private readonly string vsstub;
        private static readonly string WindowsNewline = "\r\n";

        static SolutionSynchronizer()
        {
            Dictionary<string, ScriptingLanguage> dictionary = new Dictionary<string, ScriptingLanguage>();
            dictionary.Add(".cs", ScriptingLanguage.CSharp);
            dictionary.Add(".js", ScriptingLanguage.UnityScript);
            dictionary.Add(".boo", ScriptingLanguage.Boo);
            dictionary.Add(".xml", ScriptingLanguage.None);
            dictionary.Add(".shader", ScriptingLanguage.None);
            dictionary.Add(".compute", ScriptingLanguage.None);
            dictionary.Add(".cginc", ScriptingLanguage.None);
            dictionary.Add(".glslinc", ScriptingLanguage.None);
            dictionary.Add(".txt", ScriptingLanguage.None);
            dictionary.Add(".cd", ScriptingLanguage.None);
            dictionary.Add("cs", ScriptingLanguage.CSharp);
            dictionary.Add("js", ScriptingLanguage.UnityScript);
            dictionary.Add("boo", ScriptingLanguage.Boo);
            dictionary.Add("xml", ScriptingLanguage.None);
            dictionary.Add("shader", ScriptingLanguage.None);
            dictionary.Add("compute", ScriptingLanguage.None);
            dictionary.Add("cginc", ScriptingLanguage.None);
            dictionary.Add("glslinc", ScriptingLanguage.None);
            dictionary.Add("txt", ScriptingLanguage.None);
            dictionary.Add("cd", ScriptingLanguage.None);
            SupportedExtensions = dictionary;
            Dictionary<ScriptingLanguage, string> dictionary2 = new Dictionary<ScriptingLanguage, string>();
            dictionary2.Add(ScriptingLanguage.Boo, ".booproj");
            dictionary2.Add(ScriptingLanguage.CSharp, ".csproj");
            dictionary2.Add(ScriptingLanguage.UnityScript, ".unityproj");
            dictionary2.Add(ScriptingLanguage.None, ".csproj");
            ProjectExtensions = dictionary2;
            _MonoDevelopPropertyHeader = new Regex(@"^\s*GlobalSection\(MonoDevelopProperties.*\)");
            MSBuildNamespaceUri = "http://schemas.microsoft.com/developer/msbuild/2003";
            string[] textArray1 = new string[] { 
                "GlobalSection(MonoDevelopProperties) = preSolution", "\t\tStartupItem = Assembly-CSharp.csproj", "\t\tPolicies = $0", "\t\t$0.TextStylePolicy = $1", "\t\t$1.inheritsSet = null", "\t\t$1.scope = text/x-csharp", "\t\t$0.CSharpFormattingPolicy = $2", "\t\t$2.inheritsSet = Mono", "\t\t$2.inheritsScope = text/x-csharp", "\t\t$2.scope = text/x-csharp", "\t\t$0.TextStylePolicy = $3", "\t\t$3.FileWidth = 120", "\t\t$3.TabWidth = 4", "\t\t$3.EolMarker = Unix", "\t\t$3.inheritsSet = Mono", "\t\t$3.inheritsScope = text/plain", 
                "\t\t$3.scope = text/plain", "\tEndGlobalSection"
             };
            DefaultMonoDevelopSolutionProperties = string.Join("\r\n", textArray1);
            scriptReferenceExpression = new Regex("^Library.ScriptAssemblies.(?<project>Assembly-(?<language>[^-]+)(?<editor>-Editor)?(?<firstpass>-firstpass)?).dll$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
        }

        public SolutionSynchronizer(string projectDirectory) : this(projectDirectory, DefaultSynchronizationSettings)
        {
        }

        public SolutionSynchronizer(string projectDirectory, ISolutionSynchronizationSettings settings)
        {
            this.vsstub = "-vs";
            this._projectDirectory = projectDirectory;
            this._settings = settings;
            this._projectName = Path.GetFileName(this._projectDirectory);
            this._pathCache = new Dictionary<string, string>();
        }

        private string AssetsFolder()
        {
            return Path.Combine(this._projectDirectory, "Assets");
        }

        private static void DumpIsland(MonoIsland island)
        {
            Console.WriteLine("{0} ({1})", island._output, island._classlib_profile);
            Console.WriteLine("Files: ");
            Console.WriteLine(string.Join("\n", island._files));
            Console.WriteLine("References: ");
            Console.WriteLine(string.Join("\n", island._references));
            Console.WriteLine(string.Empty);
        }

        private string EscapedRelativePathFor(FileSystemInfo file)
        {
            return this.EscapedRelativePathFor(file.FullName);
        }

        private string EscapedRelativePathFor(string file)
        {
            string str = this._projectDirectory.Replace("/", @"\");
            file = file.Replace("/", @"\");
            return SecurityElement.Escape(!file.StartsWith(str) ? file : file.Substring(this._projectDirectory.Length + 1));
        }

        private string GenerateAllAssetProjectPart()
        {
            StringBuilder builder = new StringBuilder();
            foreach (string str in AssetDatabase.GetAllAssetPaths())
            {
                string extension = Path.GetExtension(str);
                if (SupportedExtensions.ContainsKey(extension) && (((ScriptingLanguage) SupportedExtensions[extension]) == ScriptingLanguage.None))
                {
                    builder.AppendFormat("     <None Include=\"{0}\" />{1}", this.EscapedRelativePathFor(str), WindowsNewline);
                }
            }
            return builder.ToString();
        }

        private string GetProjectActiveConfigurations(string projectGuid)
        {
            return string.Format(DefaultSynchronizationSettings.SolutionProjectConfigurationTemplate, projectGuid);
        }

        private string GetProjectEntries(IEnumerable<MonoIsland> islands, bool forVisualStudio)
        {
            <GetProjectEntries>c__AnonStorey50 storey = new <GetProjectEntries>c__AnonStorey50 {
                forVisualStudio = forVisualStudio,
                <>f__this = this
            };
            IEnumerable<string> source = Enumerable.Select<MonoIsland, string>(islands, new Func<MonoIsland, string>(storey.<>m__AA));
            return string.Join(WindowsNewline, source.ToArray<string>());
        }

        public static string GetProjectExtension(ScriptingLanguage language)
        {
            if (!ProjectExtensions.ContainsKey(language))
            {
                throw new ArgumentException("Unsupported language", "language");
            }
            return ProjectExtensions[language];
        }

        private string GetProperDirectoryCapitalization(DirectoryInfo dirInfo)
        {
            <GetProperDirectoryCapitalization>c__AnonStorey4E storeye = new <GetProperDirectoryCapitalization>c__AnonStorey4E {
                dirInfo = dirInfo
            };
            if (storeye.dirInfo.FullName == this._projectDirectory)
            {
                return storeye.dirInfo.FullName;
            }
            if (this._pathCache.ContainsKey(storeye.dirInfo.FullName))
            {
                return this._pathCache[storeye.dirInfo.FullName];
            }
            DirectoryInfo parent = storeye.dirInfo.Parent;
            if (parent == null)
            {
                return storeye.dirInfo.FullName.ToUpperInvariant();
            }
            string str = Path.Combine(this.GetProperDirectoryCapitalization(parent), Enumerable.First<DirectoryInfo>(parent.GetDirectories(), new Func<DirectoryInfo, bool>(storeye.<>m__A7)).Name);
            this._pathCache[storeye.dirInfo.FullName] = str;
            return str;
        }

        public static bool IsManagedAssembly(string file)
        {
            try
            {
                using (Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    BinaryReader reader = new BinaryReader(stream);
                    stream.Position = 60L;
                    uint num = reader.ReadUInt32();
                    stream.Position = num + 0xe8;
                    return (0L != reader.ReadUInt64());
                }
            }
            catch
            {
            }
            return false;
        }

        public bool ProjectExists(MonoIsland island)
        {
            return File.Exists(this.ProjectFile(island, false));
        }

        public string ProjectFile(MonoIsland island, bool forVisualStudio)
        {
            ScriptingLanguage language = SupportedExtensions[island.GetExtensionOfSourceFiles()];
            string str = !forVisualStudio ? string.Empty : this.vsstub;
            return Path.Combine(this._projectDirectory, string.Format("{0}{1}{2}", Path.GetFileNameWithoutExtension(island._output), str, ProjectExtensions[language]));
        }

        private string ProjectFooter(MonoIsland island)
        {
            ScriptingLanguage language = SupportedExtensions[island.GetExtensionOfSourceFiles()];
            return string.Format(this._settings.GetProjectFooterTemplate(language), this.ReadExistingMonoDevelopProjectProperties(island));
        }

        private string ProjectGuid(string assembly)
        {
            return SolutionGuidGenerator.GuidForProject(this._projectName + Path.GetFileNameWithoutExtension(assembly));
        }

        private string ProjectHeader(MonoIsland island)
        {
            string str = "4.0";
            string str2 = "10.0.20506";
            ScriptingLanguage language = SupportedExtensions[island.GetExtensionOfSourceFiles()];
            if (this._settings.VisualStudioVersion == 9)
            {
                str = "3.5";
                str2 = "9.0.21022";
            }
            object[] args = new object[] { str, str2, this.ProjectGuid(island._output), this._settings.EngineAssemblyPath, this._settings.EditorAssemblyPath, string.Join(";", this._settings.Defines.Union<string>(island._defines).ToArray<string>()), MSBuildNamespaceUri, Path.GetFileNameWithoutExtension(island._output) };
            return string.Format(this._settings.GetProjectHeaderTemplate(language), args);
        }

        private string ProjectText(MonoIsland island, bool forVisualStudio, string allAssetsProject)
        {
            StringBuilder builder = new StringBuilder(this.ProjectHeader(island));
            List<string> first = new List<string>();
            List<Match> list2 = new List<Match>();
            foreach (string str3 in island._files)
            {
                string str = Path.GetExtension(str3).ToLower();
                string file = !Path.IsPathRooted(str3) ? Path.Combine(this._projectDirectory, str3) : str3;
                if (".dll" != str)
                {
                    string str4 = "Compile";
                    builder.AppendFormat("     <{0} Include=\"{1}\" />{2}", str4, this.EscapedRelativePathFor(file), WindowsNewline);
                }
                else
                {
                    first.Add(file);
                }
            }
            builder.Append(allAssetsProject);
            IEnumerator<string> enumerator = first.Union<string>(island._references).GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    string current = enumerator.Current;
                    if (((!current.EndsWith("/UnityEditor.dll") && !current.EndsWith("/UnityEngine.dll")) && !current.EndsWith(@"\UnityEditor.dll")) && !current.EndsWith(@"\UnityEngine.dll"))
                    {
                        Match match;
                        if ((((match = scriptReferenceExpression.Match(current)) != null) && match.Success) && (!forVisualStudio || (((int) Enum.Parse(typeof(ScriptingLanguage), match.Groups["language"].Value, true)) == 2)))
                        {
                            list2.Add(match);
                        }
                        else
                        {
                            string str6 = !Path.IsPathRooted(current) ? Path.Combine(this._projectDirectory, current) : current;
                            if (IsManagedAssembly(str6))
                            {
                                string path = this.EscapedRelativePathFor(str6);
                                builder.AppendFormat(" <Reference Include=\"{0}\">{1}", Path.GetFileNameWithoutExtension(path), WindowsNewline);
                                builder.AppendFormat(" <HintPath>{0}</HintPath>{1}", path, WindowsNewline);
                                builder.AppendLine(" </Reference>");
                            }
                        }
                    }
                }
            }
            finally
            {
                if (enumerator == null)
                {
                }
                enumerator.Dispose();
            }
            if (0 < list2.Count)
            {
                builder.AppendLine("  </ItemGroup>");
                builder.AppendLine("  <ItemGroup>");
                foreach (Match match2 in list2)
                {
                    string str8 = match2.Groups["project"].Value;
                    if (forVisualStudio)
                    {
                        str8 = str8 + this.vsstub;
                    }
                    builder.AppendFormat("    <ProjectReference Include=\"{0}{1}\">{2}", str8, GetProjectExtension((ScriptingLanguage) ((int) Enum.Parse(typeof(ScriptingLanguage), match2.Groups["language"].Value, true))), WindowsNewline);
                    builder.AppendFormat("      <Project>{{{0}}}</Project>", this.ProjectGuid(Path.Combine("Temp", match2.Groups["project"].Value + ".dll")), WindowsNewline);
                    builder.AppendFormat("      <Name>{0}</Name>", str8, WindowsNewline);
                    builder.AppendLine("    </ProjectReference>");
                }
            }
            builder.Append(this.ProjectFooter(island));
            return builder.ToString();
        }

        private string ReadExistingMonoDevelopProjectProperties(MonoIsland island)
        {
            XmlNamespaceManager manager;
            if (!this.ProjectExists(island))
            {
                return string.Empty;
            }
            XmlDocument document = new XmlDocument();
            try
            {
                document.Load(this.ProjectFile(island, false));
                manager = new XmlNamespaceManager(document.NameTable);
                manager.AddNamespace("msb", MSBuildNamespaceUri);
            }
            catch (System.Exception exception)
            {
                if (!(exception is IOException) && !(exception is XmlException))
                {
                    throw;
                }
                return string.Empty;
            }
            XmlNodeList list = document.SelectNodes("/msb:Project/msb:ProjectExtensions", manager);
            if (list.Count == 0)
            {
                return string.Empty;
            }
            StringBuilder builder = new StringBuilder();
            IEnumerator enumerator = list.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    XmlNode current = (XmlNode) enumerator.Current;
                    builder.AppendLine(current.OuterXml);
                }
            }
            finally
            {
                IDisposable disposable = enumerator as IDisposable;
                if (disposable == null)
                {
                }
                disposable.Dispose();
            }
            return builder.ToString();
        }

        private string ReadExistingMonoDevelopSolutionProperties()
        {
            if (this.SolutionExists())
            {
                string[] strArray;
                try
                {
                    strArray = File.ReadAllLines(this.SolutionFile(false));
                }
                catch (IOException)
                {
                    return DefaultMonoDevelopSolutionProperties;
                }
                StringBuilder builder = new StringBuilder();
                bool flag = false;
                foreach (string str in strArray)
                {
                    if (_MonoDevelopPropertyHeader.IsMatch(str))
                    {
                        flag = true;
                    }
                    if (flag)
                    {
                        builder.AppendFormat("{0}{1}", str, WindowsNewline);
                        if (str.Contains("EndGlobalSection"))
                        {
                            flag = false;
                        }
                    }
                }
                if (0 < builder.Length)
                {
                    return builder.ToString();
                }
            }
            return DefaultMonoDevelopSolutionProperties;
        }

        public bool ShouldFileBePartOfSolution(string file)
        {
            string extension = Path.GetExtension(file);
            return ((extension == ".dll") || SupportedExtensions.ContainsKey(extension));
        }

        public bool SolutionExists()
        {
            return File.Exists(this.SolutionFile(false));
        }

        internal string SolutionFile(bool onlyCSharp)
        {
            string str = !onlyCSharp ? string.Empty : "-csharp";
            return Path.Combine(this._projectDirectory, string.Format("{0}{1}.sln", this._projectName, str));
        }

        private string SolutionGuid()
        {
            return SolutionGuidGenerator.GuidForSolution(this._projectName);
        }

        private string SolutionText(IEnumerable<MonoIsland> islands, bool onlyCSharp)
        {
            <SolutionText>c__AnonStorey4F storeyf = new <SolutionText>c__AnonStorey4F {
                onlyCSharp = onlyCSharp,
                <>f__this = this
            };
            string str = "11.00";
            if (this._settings.VisualStudioVersion == 9)
            {
                str = "10.00";
            }
            IEnumerable<MonoIsland> enumerable = Enumerable.Where<MonoIsland>(islands, new Func<MonoIsland, bool>(storeyf.<>m__A8));
            string projectEntries = this.GetProjectEntries(enumerable, storeyf.onlyCSharp);
            string str3 = string.Join(WindowsNewline, Enumerable.Select<MonoIsland, string>(enumerable, new Func<MonoIsland, string>(storeyf.<>m__A9)).ToArray<string>());
            object[] args = new object[] { str, projectEntries, str3, this.ReadExistingMonoDevelopSolutionProperties() };
            return string.Format(this._settings.SolutionTemplate, args);
        }

        public void Sync()
        {
            if (<>f__am$cacheD == null)
            {
                <>f__am$cacheD = i => 0 < i._files.Length;
            }
            IEnumerable<MonoIsland> islands = Enumerable.Where<MonoIsland>(InternalEditorUtility.GetMonoIslands(), <>f__am$cacheD);
            string otherAssetsProjectPart = this.GenerateAllAssetProjectPart();
            this.SyncSolution(islands);
            IEnumerator<MonoIsland> enumerator = islands.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    MonoIsland current = enumerator.Current;
                    this.SyncProject(current, otherAssetsProjectPart);
                }
            }
            finally
            {
                if (enumerator == null)
                {
                }
                enumerator.Dispose();
            }
        }

        private static void SyncFileIfNotChanged(string filename, string newContents)
        {
            if (!File.Exists(filename) || (newContents != File.ReadAllText(filename)))
            {
                File.WriteAllText(filename, newContents);
            }
        }

        public bool SyncIfNeeded(IEnumerable<string> affectedFiles)
        {
            if (this.SolutionExists() && Enumerable.Any<string>(affectedFiles, new Func<string, bool>(this.ShouldFileBePartOfSolution)))
            {
                this.Sync();
                return true;
            }
            return false;
        }

        private void SyncProject(MonoIsland island, string otherAssetsProjectPart)
        {
            SyncFileIfNotChanged(this.ProjectFile(island, false), this.ProjectText(island, false, otherAssetsProjectPart));
            SyncFileIfNotChanged(this.ProjectFile(island, true), this.ProjectText(island, true, otherAssetsProjectPart));
        }

        private void SyncSolution(IEnumerable<MonoIsland> islands)
        {
            SyncFileIfNotChanged(this.SolutionFile(false), this.SolutionText(islands, false));
            SyncFileIfNotChanged(this.SolutionFile(true), this.SolutionText(islands, true));
        }

        [CompilerGenerated]
        private sealed class <GetProjectEntries>c__AnonStorey50
        {
            internal SolutionSynchronizer <>f__this;
            internal bool forVisualStudio;

            internal string <>m__AA(MonoIsland i)
            {
                object[] args = new object[] { this.<>f__this.SolutionGuid(), this.<>f__this._projectName, Path.GetFileName(this.<>f__this.ProjectFile(i, this.forVisualStudio)), this.<>f__this.ProjectGuid(i._output) };
                return string.Format(SolutionSynchronizer.DefaultSynchronizationSettings.SolutionProjectEntryTemplate, args);
            }
        }

        [CompilerGenerated]
        private sealed class <GetProperDirectoryCapitalization>c__AnonStorey4E
        {
            internal DirectoryInfo dirInfo;

            internal bool <>m__A7(DirectoryInfo dir)
            {
                return dir.Name.Equals(this.dirInfo.Name, StringComparison.OrdinalIgnoreCase);
            }
        }

        [CompilerGenerated]
        private sealed class <SolutionText>c__AnonStorey4F
        {
            internal SolutionSynchronizer <>f__this;
            internal bool onlyCSharp;

            internal bool <>m__A8(MonoIsland i)
            {
                return (!this.onlyCSharp || (ScriptingLanguage.CSharp == ((ScriptingLanguage) SolutionSynchronizer.SupportedExtensions[i.GetExtensionOfSourceFiles()])));
            }

            internal string <>m__A9(MonoIsland i)
            {
                return this.<>f__this.GetProjectActiveConfigurations(this.<>f__this.ProjectGuid(i._output));
            }
        }
    }
}

